package com.itlong.cloud.intercept.sdkV2;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowItem;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.fastjson.JSON;
import com.itlong.cloud.POJO.DTO.common.HotObject;
import com.itlong.cloud.POJO.PO.HotParamFlowRule;
import com.itlong.cloud.POJO.PO.PropertyUserInfoPO;
import com.itlong.cloud.POJO.VO.MessageVO;
import com.itlong.cloud.PlatformConstants;
import com.itlong.cloud.enumerate.ecode.SDKErrorCodeEnum;
import com.itlong.cloud.equip.sdk.IHotParamFlowRuleClient;
import com.itlong.cloud.service.RedisService;
import com.itlong.cloud.user.sdk.ICloudTalkSDKAppUserServiceClient;
import com.itlong.cloud.utils.words.StringHandlerUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * <desc>
 * 阿里Sentinel拦截器限流
 * </desc>
 *
 * @author: chengshican
 * @Date: 2020-11-23 16:56
 */


@Aspect
@Component
@Order(0)
public class SDKSentinelInterceptor {
    private static Logger LOG = LoggerFactory.getLogger(SDKSentinelInterceptor.class);


//    //限流缓存时间
//    @Value("${" + PlatformConstants.SDK_REDIS_TIME + "}")
//    private long SDK_REDIS_TIME;


    @Autowired
    RedisService redisService;

    @Autowired
    IHotParamFlowRuleClient iHotParamFlowRuleClient;

    @Autowired
    ICloudTalkSDKAppUserServiceClient iCloudTalkSDKAppUserServiceClient;

    // 定义一个切入点 v1版本接口存在不确定因素先注销
   // @Pointcut("(execution(* com.itlong.cloud.controller.sdkV2..*.*(..)) || execution(* com.itlong.cloud.controller.sdkV1..*.*(..)) ) && !@annotation(com.itlong.cloud.annotation.UnAuthAccessRequired)")
    // 定义一个切入点
    @Pointcut("(execution(* com.itlong.cloud.controller.sdkV2..*.*(..))) && !@annotation(com.itlong.cloud.annotation.UnAuthAccessRequired)")
    public void excudeService() {
    }

//    @Around("excudeService()")
    public Object twiceAsOld(ProceedingJoinPoint thisJoinPoint) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//        LOG.info("*********请求的url地址***=>{}",request.getRequestURI());
        List<HotObject> list = new ArrayList<>();
        String RESOURCE_NAME = request.getRequestURI();
        String token = request.getParameter("token");

        PropertyUserInfoPO propertyUserInfoPO = iCloudTalkSDKAppUserServiceClient.developerRetrieve(token);
        String userId = null;
        if (propertyUserInfoPO != null) {
            userId = propertyUserInfoPO.getUserId();//1010487361143996416
//            LOG.info("====userId=====>{}",userId);
        }
        //获取缓存中的限流规则
        List<HotParamFlowRule> ruleList = this.getFlowRule(RESOURCE_NAME);
        //组装限流规则
        for (HotParamFlowRule hotParamFlowRule : ruleList) {
            HotObject hotObject = new HotObject();
            hotObject.setHotObject(hotParamFlowRule.getHotObject());
            hotObject.setHotClassType(hotParamFlowRule.getHotClassType());
            hotObject.setHotParamCount(hotParamFlowRule.getHotParamCount());
            list.add(hotObject);
        }
//        LOG.info("======传入热点规则的ruleList===============>{}",ruleList);
        //规则初始化
        initParamFlowRule(ruleList.get(0).getResourceNameUrl(), ruleList.get(0).getSysCount(), ruleList.get(0).getDurationInsec(), ruleList.get(0).getHotParamIdx(), list, ruleList.get(0).getMaxQueueingTimeMs(), ruleList.get(0).getControlBehavior());

        Entry entry = null;
        try {
            entry = SphU.entry(RESOURCE_NAME, EntryType.IN, 1, userId);
            Date t = new Date();
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            LOG.info(String.format("\n【SDK限流用户】====>:%s: 的请求限流成功，当前时间:%s===================================================\n", userId, df.format(t)));
            return thisJoinPoint.proceed();
        } catch (BlockException e) {
            Date t = new Date();
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            LOG.info(String.format("\n【SDK限流,系统繁忙】======>系统繁忙请稍后重试========>当前时间：%s=============================================\n", df.format(t)));
            return new MessageVO(SDKErrorCodeEnum.SDK_RATE_LIMIT_ERR.getErrorCode());
        } finally {
            if (entry != null) {
                entry.exit();
            }
        }
    }


    /**
     * <desc>
     * 处理SDK请求路由获取限流规则
     * </desc>
     *
     * @Author chengshican
     * @Date 2020-11-25 14:58
     * @Param resourceNameUrl 路由
     * @Return ruleList 限流规则的集合
     * @Exception
     */
    private List<HotParamFlowRule> getFlowRule(String resourceNameUrl) {
        //3.读取缓存中的数据
        List<HotParamFlowRule> ruleList = null;
        int flag = 0;
        while (flag == 0) {
            String paramFlowRule = redisService.get(resourceNameUrl);
            if (null == paramFlowRule) {
                //1通过请求的url地址 拿到地址的父类  比如 /sdk 代表sdk服务
                //2 查询当前请求地址资源的热点限流规则
                //3.进行热点限流规则的判断是否配置有热点限流规则，如果有采用热点限流规则，如果没有，采用父类的系统规则配置
                Map<String, Object> map = new HashMap<>();
                map.put("resourceNameUrl", resourceNameUrl);
                //查询当前请求地址资源的热点限流规则
                ruleList = iHotParamFlowRuleClient.getHotParamFlowRule(map);
                if (ruleList.size() != 0) {
                    redisService.set(resourceNameUrl, PlatformConstants.SDK_REDIS_TIME, ruleList);
                } else {
                    //通过请求的url地址 拿到地址的父类  比如 /sdk 代表sdk服务     sdk服务中的接口路由 父类大概分为 /sdk,/jiabanjing,/cloud,/projectManage
                    if (resourceNameUrl.indexOf("/sdk") == 0 || resourceNameUrl.indexOf("/jiabanjing") == 0 ||
                            resourceNameUrl.indexOf("/cloud") == 0 || resourceNameUrl.indexOf("/projectManage") == 0) {
                        Map<String, Object> param = new HashMap<>();
                        param.put("resourceNameUrl", "/sdk");
                        param.put("layer", "0");
                        //读取父类的限流规则
                        // 查询当前请求地址资源的热点限流规则
                        HotParamFlowRule flowRule = iHotParamFlowRuleClient.getFlowRuleByParam(param);
                        //将父类的限流规则添加到限流规则集合中
                        flowRule.setResourceNameUrl(resourceNameUrl);
                        flowRule.setHotParamIdx(0);
                        flowRule.setHotParamCount(0);
                        flowRule.setHotClassType("3");
                        flowRule.setHotObject("admin");
                        ruleList.add(flowRule);
                        //添加到缓存中
                        redisService.set(resourceNameUrl, PlatformConstants.SDK_REDIS_TIME, ruleList);
                    }
                }
//                LOG.info("======数据库中===============>{}",ruleList);
            } else {
//                LOG.info("======缓存中===============>{}",paramFlowRule);
                ruleList = JSON.parseArray(paramFlowRule, HotParamFlowRule.class);
//                LOG.info("======ruleList===============>{}",ruleList);
            }
            flag = flag + 1;
//            LOG.info("======flag===============>{}",flag);
        }
        return ruleList;
    }


    /**
     * <desc>
     * 限流规则初始化
     * </desc>
     *
     * @Author chengshican
     * @Date 2020-11-25 14:40
     * @Param
     * @Return
     * @Exception
     */

    //热点参数限流规则
    private static void initParamFlowRule(String resourceName, double sysCount, long durationInSec, Integer hotParamIdx, List<HotObject> hotObjectList, Integer maxQueueingTimeMs, String controlBehavior) {
        List<ParamFlowRule> rules = new ArrayList<>();
        if (hotObjectList.size() != 0) {
            ParamFlowRule rule = new ParamFlowRule();
            //阈值类型：只支持QPS
            rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
            //阈值
            // rule.setCount(1);
            rule.setCount(sysCount);
            //资源名
            rule.setResource(resourceName);
            //统计窗口时间长度
            rule.setDurationInSec(durationInSec);
            //0:立即拒绝掉
            if ("0".equals(controlBehavior)) {
                rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
            } else if ("1".equals(controlBehavior)) {//1:请求匀速排队
                // 这里设置的等待处理时间较大, 让系统能平稳的处理所有的请求
                //rule.setMaxQueueingTimeMs(20 * 1000);// 表示每一个请求的最长等待时间20s
                rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
                rule.setMaxQueueingTimeMs(maxQueueingTimeMs);
            }
            //进行热点参数限流规则配置
            List<ParamFlowItem> items = new ArrayList<>();
            for (HotObject hotObject : hotObjectList) {
                if (StringHandlerUtil.allFieldIsNotNull(hotObject)) {
                    ParamFlowItem item = new ParamFlowItem();
                    if (hotObject.getHotParamCount() != null && hotObject.getHotClassType() != null && hotObject.getHotObject() != null) {
                        //热点参数的类型
                        switch (hotObject.getHotClassType()) {
                            case "1":
                                item.setClassType(int.class.getTypeName());
                                break;
                            case "2":
                                item.setClassType(double.class.getTypeName());
                                break;
                            case "3":
                                item.setClassType(String.class.getTypeName());
                                break;
                            case "4":
                                item.setClassType(long.class.getTypeName());
                                break;
                            case "5":
                                item.setClassType(float.class.getTypeName());
                                break;
                            case "6":
                                item.setClassType(char.class.getTypeName());
                                break;
                            case "7":
                                item.setClassType(byte.class.getTypeName());
                                break;
                        }
                        //热点参数阈值数
                        item.setCount(hotObject.getHotParamCount());
                        ////需要统计的值
                        item.setObject(hotObject.getHotObject().toString());


                    }
                    //指配热点参数的下标(默认是0)
                    rule.setParamIdx(hotParamIdx);
                    items.add(item);
                }
            }
            rule.setParamFlowItemList(items);
            rules.add(rule);
            ParamFlowRuleManager.loadRules(rules);
        } else {
            //配置默认的系统限流规则
            List<FlowRule> flowRules = new ArrayList<>();
            FlowRule rule = new FlowRule();
            rule.setResource(resourceName);
            rule.setCount(sysCount);
            rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
            rule.setLimitApp("default");
            flowRules.add(rule);
            FlowRuleManager.loadRules(flowRules);
        }
    }
}
